import { RuntimeModelError, toNumberOrNil } from '@ibiz-template/core';
import {
  IChartSeriesCSCartesian2DEncode,
  IDEChartSeries,
} from '@ibiz/model-core';
import { ECElementEvent, SeriesOption } from 'echarts';
import { isNil, mergeDeepRight } from 'ramda';
import {
  ChartOptionsGenerator,
  parseUserParams,
} from './chart-options-generator';

/** 序列的单条数据 */
export type SingleData = {
  /**
   * 值属性的值
   */
  value: number;

  /**
   * 完整的实体数据
   * @author lxm
   * @date 2023-06-09 09:31:27
   * @type {IData}
   */
  deData?: IData;
};
/** 分类数据，key是分类属性的值 */
export type CatalogData = Record<string, SingleData>;

/** 分组数据，key是分组属性的值 */
export type GroupData = Record<string, CatalogData>;

export const DEFAULT_GROUP = '$default_group';

export class BaseSeriesGenerator<T extends IDEChartSeries = IDEChartSeries> {
  /**
   * 分类属性(小写)
   * @author lxm
   * @date 2023-06-09 02:44:57
   * @type {string}
   */
  catalogField: string;

  /**
   * 值属性（小写）
   * @author lxm
   * @date 2023-06-09 02:45:15
   * @type {string}
   */
  valueField: string;

  /**
   * 分组属性（小写）
   * @author lxm
   * @date 2023-06-09 02:46:05
   * @type {string}
   */
  groupField?: string;

  /**
   * X轴坐标索引
   * @author lxm
   * @date 2023-06-09 02:47:54
   * @type {number}
   */
  xAxisIndex?: number;

  /**
   * y轴坐标索引
   * @author lxm
   * @date 2023-06-09 02:48:13
   * @type {number}
   */
  yAxisIndex?: number;

  /**
   * 序列名称
   * @author lxm
   * @date 2023-06-09 02:49:35
   * @type {string}
   */
  seriesName: string;

  /**
   * 根据后台数据处理出来的分组数据
   * @author lxm
   * @date 2023-06-09 02:58:28
   * @type {GroupData}
   */
  groupData?: GroupData;

  /**
   * 静态的序列options
   * @author lxm
   * @date 2023-06-09 03:08:47
   * @type {SeriesOption}
   */
  staticOptions: SeriesOption = {};

  /**
   * 序列的自定义Options
   * @author lxm
   * @date 2023-06-09 08:59:40
   * @type {SeriesOption}
   */
  seriesUserParam?: SeriesOption;

  /**
   * 是否根据代码表自动补全分类
   * @author lxm
   * @date 2023-06-09 09:28:04
   * @type {boolean}
   */
  autoCompleteCategory: boolean = true;

  /**
   * Creates an instance of BaseSeriesGenerator.
   * @author lxm
   * @date 2023-06-09 02:46:54
   * @param {T} model 序列模型
   * @param {ChartOptionsGenerator} chartGenerator 图表生成器
   */
  constructor(
    public model: T,
    protected chartGenerator: ChartOptionsGenerator,
  ) {
    this.model = model;
    this.chartGenerator = chartGenerator;

    const { chartSeriesEncode, caption, id, userParam } = model;
    if (!model.catalogField) {
      throw new RuntimeModelError(model, '缺少分类属性配置');
    }
    if (!model.valueField) {
      throw new RuntimeModelError(model, '缺少值属性配置');
    }
    this.catalogField = model.catalogField!.toLowerCase();
    this.valueField = model.valueField!.toLowerCase();
    this.groupField = model.seriesField?.toLowerCase();

    const { chartXAxisId, chartYAxisId } = (chartSeriesEncode ||
      {}) as IChartSeriesCSCartesian2DEncode;
    this.xAxisIndex = toNumberOrNil(chartXAxisId);
    this.yAxisIndex = toNumberOrNil(chartYAxisId);
    this.seriesName = caption || id!;

    this.staticOptions = this.calcStaticOptions();

    if (userParam) {
      this.seriesUserParam = parseUserParams(userParam);
    }
  }

  /**
   * 计算静态序列的options
   * @author lxm
   * @date 2023-06-09 03:09:34
   */
  protected calcStaticOptions(): SeriesOption {
    const options: SeriesOption = {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      type: this.model.seriesType! as any,
      name: this.seriesName,
      emphasis: {
        label: {
          fontSize: 20,
          show: true,
        },
      },
      label: {
        position: 'top',
        show: true,
      },
    };
    return options;
  }

  /**
   * 有代码就转换，值为空直接返回空
   *
   * @author lxm
   * @date 2023-06-09 08:42:50
   * @author lxm
   * @date 2023-06-09 08:46:00
   * @param {(string | undefined)} codeListKey 代码表值
   * @param {(string | undefined)} val 数据值
   * @param {boolean} isExclude 是否排除非代码表的值，true时，匹配不到代码表返回undefined，反之返回原值
   * @return {*}  {(string | undefined)}
   */
  translateVal(
    codeListKey: string | undefined,
    val: string | undefined,
    isExclude: boolean = false,
  ): string | undefined {
    if (isNil(val)) {
      return undefined;
    }
    if (isNil(codeListKey)) {
      return val;
    }
    const codeListItems = this.chartGenerator.codeListMap.get(codeListKey);
    if (codeListItems?.length) {
      const find = codeListItems.find(x => x.value === val);
      if (find) {
        return find.text;
      }
    }
    return isExclude ? undefined : val;
  }

  /**
   * 计算分组数据
   * @author lxm
   * @date 2023-06-09 03:42:53
   * @param {IData[]} data
   * @return {*}  {GroupData}
   */
  protected calcGroupData(data: IData[]): GroupData {
    // 清空分组数据
    this.groupData = {};
    const groupData = this.groupData!;
    // 分组属性代码表和分类属性代码表
    const { seriesCodeListId, catalogCodeListId } = this.model;

    data.forEach(item => {
      // 计算分组名称标识
      let group = DEFAULT_GROUP;
      if (this.groupField) {
        const groupVal = this.translateVal(
          seriesCodeListId,
          item[this.groupField],
        );
        // 存在分组属性，但是分组属性没值的数据排除在外
        if (isNil(groupVal)) {
          return;
        }
        group = groupVal;
      }

      // 分组对应的分类数据不存在时新建一个
      if (!groupData[group]) {
        groupData[group] = {};

        // 有代码表的时候补全代码表项个数的数据条数
        if (catalogCodeListId && this.autoCompleteCategory) {
          const codeListItems =
            this.chartGenerator.codeListMap.get(catalogCodeListId)!;
          codeListItems.forEach(x => {
            groupData[group][x.text] = {
              value: 0,
            };
          });
        }
      }

      // 分类属性值计算和转换
      const catalog = this.translateVal(
        catalogCodeListId,
        item[this.catalogField],
        true,
      );
      // 分类属性没值的数据排除
      if (!catalog) {
        return;
      }

      // 该分类属性对应的单条数据不存在时新建一个
      if (!groupData[group][catalog]) {
        groupData[group][catalog] = {
          value: 0,
        };
      }
      // 相同分类属性的值属性数据累加
      groupData[group][catalog].value += item[this.valueField];
      groupData[group][catalog].deData = item;
    });

    return groupData;
  }

  /**
   * 根据分组数据算出多个序列的options
   * 大多数图表分组都是一个分组数据生成一条series
   * @author lxm
   * @date 2023-06-09 03:34:24
   * @param {GroupData} groupData
   * @return {*}  {SeriesOption[]}
   */
  protected calcGroupSeries(groupData: GroupData): SeriesOption[] {
    return Object.keys(groupData).map(group => {
      // 处理每个序列的数据
      const catalogData = groupData[group];
      const data: IData[] = this.calcSeriesData(catalogData);

      // 合并静态的seriesOptions
      let options: IData = { ...this.staticOptions, data };

      // 非默认分组的时候name是分组属性的值
      if (group !== DEFAULT_GROUP) {
        options.name = group;
      }

      // 合并自定义参数
      if (this.seriesUserParam) {
        options = mergeDeepRight(options, this.seriesUserParam);
      }

      return options as SeriesOption;
    });
  }

  /**
   * 生成每条序列的data,由于不同图表类型格式不同所以为any
   * 默认提供的是二维数组，按[X轴，y轴]格式
   * @author lxm
   * @date 2023-06-09 03:38:07
   * @param {CatalogData} catalogData
   * @return {*}  {*}
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  protected calcSeriesData(catalogData: CatalogData): any {
    return Object.keys(catalogData).map(catalog => {
      const data = catalogData[catalog];
      return [catalog, data.value, data.deData?.srfkey];
    });
  }

  /**
   * 根据数据计算出序列的options
   * @author lxm
   * @date 2023-06-09 03:44:31
   * @param {IData[]} data
   * @return {*}  {(SeriesOption[] | SeriesOption)}
   */
  calcByData(data: IData[]): SeriesOption[] | SeriesOption {
    const groupData = this.calcGroupData(data);
    return this.calcGroupSeries(groupData);
  }

  /**
   * 通过事件的params获取主键
   * @author lxm
   * @date 2023-06-09 10:56:25
   * @param {ECElementEvent} params
   * @return {*}  {string}
   */
  getSrfkeyByParams(params: ECElementEvent): string {
    return (params.value as IData)[2];
  }
}
